package fgin

import (
	"context"
	"encoding/json"
	"errors"
	"log"
	"sync"
	"syscall"

	"fmt"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"github.com/streadway/amqp"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type Fgin struct{}

// 获取一个路由
func (f *Fgin) GetHttpServer(path string) *gin.Engine {
	// 读取配置
	readConfig(path)
	// 判断定义的协议
	if fginConfig.NetType != "http" {
		panic(fmt.Sprintf("您配置的net_http:%v,本方法适用的协议为:http", fginConfig.NetType))
	}
	// 进行router初始化
	var router *gin.Engine
	switch fginConfig.Env {
	case "pro":
		// 生产模式
		gin.SetMode(gin.ReleaseMode)
		router = gin.New()
	case "dev":
		// 调试模式
		gin.SetMode(gin.DebugMode)
		router = gin.New()
	default:
		panic(errors.New("env配置错误,只支持:pro生产模式,dev调试模式"))
	}
	// 初始化日志
	logInit()
	// 启动通用组件
	startFginPlug()
	// 增加中间件
	addMiddleWare(router)
	// 自定义组件
	PlugsHttpStart(router)
	// 探查方法
	router.GET("/ping", func(ctx *gin.Context) {
		ReturnJson(ctx, 200, "pong", nil)
	})
	return router
}

func startFginPlug() {
	var wg sync.WaitGroup
	// 1. 初始化mysql gorm
	wg.Add(1)
	go func() {
		defer wg.Done()
		initGorm()
	}()
	// 2. 初始化redis
	wg.Add(1)
	go func() {
		defer wg.Done()
		initRedis()
	}()
	// 3. grpc客户端
	wg.Add(1)
	go func() {
		defer wg.Done()
		initGrpcClientV2()
	}()
	// 4. rabbitmq队列初始化
	wg.Add(1)
	go func() {
		defer wg.Done()
		initRabbitmq()
	}()
	// 5. jaeger初始化
	wg.Add(1)
	go func() {
		defer wg.Done()
		initJaeger()
	}()
	// 6. prometheus初始化
	wg.Add(1)
	go func() {
		defer wg.Done()
		initPrometheus()
	}()
	// 7. 神盾启动
	wg.Add(1)
	go func() {
		defer wg.Done()
		initAegis()
	}()
	// 8. 独立校验器
	wg.Add(1)
	go func() {
		defer wg.Done()
		initValidator()
	}()
	wg.Wait()
}

// 优雅启动
func (f *Fgin) Run(router *gin.Engine) {
	if fginConfig.Ip == "" {
		logSuger.Warn("ip没有配置,使用默认ip:127.0.0.1")
		fginConfig.Ip = "127.0.0.1"
	}
	if fginConfig.Port == 0 {
		logSuger.Warn("port没有配置,使用默认port:8080")
		fginConfig.Port = 8080
	}
	srv := &http.Server{
		Addr:    fmt.Sprintf("%s:%v", fginConfig.Ip, fginConfig.Port),
		Handler: router,
	}

	go func() {
		// 服务连接
		logSuger.Info(fmt.Sprintf("http服务启动: %s:%v", fginConfig.Ip, fginConfig.Port))
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %v\n", err)
		}
	}()

	// 等待中断信号以优雅地关闭服务器（设置 5 秒的超时时间）
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
	<-quit
	logSuger.Info("Http Shutdown Server ...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	defer rabbitmqClose()
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatalf("Http Server Shutdown:%v", err)
	}
	logSuger.Info("Http Server exiting")
}

// 获得config
func (f *Fgin) GetConfig() Config {
	return fginConfig
}

// 获得日志操作
func (f *Fgin) GetLogger() *zap.SugaredLogger {
	if fginConfig.Log.Rotate != "" {
		logSugerBase.Warn("已配置了日志分割,请使用GetFginLogger获取日志对象")
	}
	return logSugerBase
}

// 获取fgin日志操作
func (f *Fgin) GetFginLogger() *FginSugaredLogger {
	return logSuger
}

// 获得数据库连接
func (f *Fgin) GetGormDb(name string) *gorm.DB {
	v, ok := fdbMap[name]
	if !ok {
		panic("mysql服务" + name + ", 未配置")
	}
	return v
}

// 获取redis连接 默认取第一个
func (f *Fgin) GetRedisClient() redis.UniversalClient {
	return redisClient
}

// 获取redis链接 通过名称获取
func (f *Fgin) GetRedisClientByName(redisName string) (redis.UniversalClient, error) {
	redisClientFLag, isOk := redisClients[redisName]
	if isOk {
		return redisClientFLag, nil
	}
	return nil, fmt.Errorf("redis service %s is nodefind", redisName)
}

// 获取grpc已经dail的conn
func (f *Fgin) GetFgrpcClientConn(serviceName string) (*FgrpcClientConn, error) {
	v, ok := fgrpcClientConn[serviceName]
	if !ok {
		errInfo := fmt.Sprintf("没有%v连接服务", serviceName)
		return nil, errors.New(errInfo)
	}
	return v, nil
}

// 获取rabbitmq dail的conn
func (f *Fgin) GetRabbitmqConn(queueName string) (*amqp.Connection, error) {
	cfg, ok := rabbitmqCfg[queueName]
	if !ok {
		return nil, errors.New("没有配置队列" + queueName)
	}
	return cfg.RabbitmqConn, nil
}

// 获取用户自定义配置
func (f *Fgin) GetOtherConfig(cfg any) error {
	oCfgB, err := json.Marshal(fginConfig.OtherConfig)
	if err != nil {
		return err
	}
	if err = json.Unmarshal(oCfgB, cfg); err != nil {
		return err
	}
	return nil
}
